package com.kaifamiao.month05.day18;

import lombok.Data;

import java.beans.PropertyDescriptor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Locale;

@Data
public class DBUtil {

    private Connection conn;
    private PreparedStatement sta;
    private boolean autoCommit = true; // 默认是自动提交
    private ThreadLocal<Connection> local = new ThreadLocal<>();
    private MyDataSource dateSource = new MyDataSource();

    public DBUtil() {
    }

    public DBUtil(MyDataSource dateSource) {
        this.dateSource = dateSource;
    }

    public DBUtil(boolean autoCommit) {
        this.autoCommit = autoCommit;
    }

    public Connection getConnection() {
        try {
            synchronized (this) { // 确保多线程也不会创建多个 connection 对象
                if (local.get() == null) { // 确保多次创建对象不会创建多个 connection 对象
                    conn = dateSource.getConnection();
                    local.set(conn);
                    conn.setAutoCommit(autoCommit);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        System.out.println("创建连接成功！");
        return conn;
    }

    public void execute(String sql, Object... params) {
        try {
            conn = getConnection();
            checkSql(sql);
            sta = conn.prepareStatement(sql);
            setObject(sta, params);
            sta.execute();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 修改数据  增加  删除 修改
     */
    public int executeUpdate(String sql, Object... params) {
        return executeUpdate(conn, sql, params);
    }

    public int executeUpdate(Connection conn, String sql, Object... params) {
        try {
            checkSql(sql);
            sta = conn.prepareStatement(sql);
            setObject(sta, params);
            return sta.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public <E> E selectOne(Class<E> cla, String sql, Object... params) {
        try {
            ResultSet set = executeQuery(sql);
            return createObject(cla, set);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private <E> E createObject(Class<E> cla, ResultSet set) throws Exception {
        E e = cla.getConstructor().newInstance(); // 获取类的实例
        int count = set.getMetaData().getColumnCount();
        if (set.next()) {
            for (int i = 1; i <= count; i++) {
                String label = set.getMetaData().getColumnName(i).toLowerCase(Locale.ROOT);
                Object value = set.getObject(i);
                PropertyDescriptor prop = new PropertyDescriptor(label, cla);
                String typeName = set.getMetaData().getColumnTypeName(i); // 获取当前列所在的类型
                Class<?> re = prop.getReadMethod().getReturnType(); // 获取返回值类型
                if ("NUMBER".equals(typeName)) {
                    if (int.class.equals(re)) { // 通过返回值类型匹配
                        value = set.getBigDecimal(i).intValue();
                    }
                    if (double.class.equals(re)) {
                        value = set.getBigDecimal(i).doubleValue();
                    }
                }
                prop.getWriteMethod().invoke(e, value);
            }
        }
        return e;
    }

    private void checkSql(String sql) throws SQLException {
        if (sql == null || sql.length() == 0) {
            throw new SQLException();
        }
    }

    private void setObject(PreparedStatement sta, Object[] params) {
        for (int i = 1; i <= params.length; i++) {
            try {
                sta.setObject(i, params[i - 1]);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 查询数据
     */
    public ResultSet executeQuery(String sql, Object... params) {
        try {
            checkSql(sql);
            conn = this.getConnection();
            sta = conn.prepareStatement(sql);
            setObject(sta, params);
            return sta.executeQuery();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void setAutoCommit(boolean autoCommit) {
        this.autoCommit = autoCommit;
    }

    public void commit() {
        if (conn != null) {
            try {
                conn.commit();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void rollback() {
        if (conn != null) {
            try {
                conn.rollback();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void close() {
        try {
            if (sta != null) {
                sta.close();
            }
            if (conn != null) {
                conn.close();
                conn = null; // 下一次创建连接的时候创建新的 connection 对象
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

